home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
016a
/
gofer221.zip
/
MACHINE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-20
|
33KB
|
1,188 lines
/* --------------------------------------------------------------------------
* machine.c: Copyright (c) Mark P Jones 1991. All rights reserved.
* See goferite.h for details and conditions of use etc...
* Gofer version 2.21 November 1991
*
* Last updated 08/11/91 mpj
*
* Graph reduction engine, code generation and execution
* ------------------------------------------------------------------------*/
#include "prelude.h"
#include "storage.h"
#include "connect.h"
#include "errors.h"
#include <setjmp.h>
/*#define DEBUG_RUN*/
/*#define DEBUG_CODE*/
Bool andorOptimise = TRUE; /* TRUE => optimise uses of &&, || */
Bool failOnError = FALSE; /* TRUE => abort as soon as error */
/* occurs */
/* --------------------------------------------------------------------------
* Data structures for machine memory (program storage):
* ------------------------------------------------------------------------*/
typedef enum {
iLOAD, iCELL, iCHAR, iINT, iFLOAT,
iSTRING, iMKAP, iUPDATE, iUPDAP, iEVAL,
iRETURN, iINTGE, iINTEQ, iINTDV, iTEST,
iGOTO, iSETSTK, iALLOC, iSLIDE, iROOT,
iDICT, iFAIL
} Instr;
typedef Int Label;
typedef union {
Int intVal;
Float floatVal;
Cell cellVal;
Text textVal;
Addr addrVal;
Instr instrVal;
Label labVal;
} MemCell;
typedef MemCell far *Memory;
static Memory memory;
#define intAt(m) memory[m].intVal
#define floatAt(m) memory[m].floatVal
#define cellAt(m) memory[m].cellVal
#define textAt(m) memory[m].textVal
#define addrAt(m) memory[m].addrVal
#define instrAt(m) memory[m].instrVal
#define labAt(m) memory[m].labVal
/* --------------------------------------------------------------------------
* Local function prototypes:
* ------------------------------------------------------------------------*/
static Void local instrNone Args((Instr));
static Void local instrInt Args((Instr,Int));
static Void local instrFloat Args((Instr,FloatPro));
static Void local instrCell Args((Instr,Cell));
static Void local instrText Args((Instr,Text));
static Void local instrLab Args((Instr,Label));
static Void local instrIntLab Args((Instr,Int,Label));
static Void local instrCellLab Args((Instr,Cell,Label));
static Void local asSTART Args((Void));
static Label local newLabel Args((Label));
static Void local asEND Args((Void));
static Void local asDICT Args((Int));
static Void local asSLIDE Args((Int));
static Void local asMKAP Args((Int));
static Void local asUPDATE Args((Int));
static Void local asGOTO Args((Label));
#ifdef DEBUG_CODE
static Void local dissassemble Args((Addr,Addr));
static Void local printCell Args((Cell));
static Addr local dissNone Args((Addr,String));
static Addr local dissInt Args((Addr,String));
static Addr local dissFloat Args((Addr,String));
static Addr local dissCell Args((Addr,String));
static Addr local dissText Args((Addr,String));
static Addr local dissAddr Args((Addr,String));
static Addr local dissIntAddr Args((Addr,String));
static Addr local dissCellAddr Args((Addr,String));
#endif
static Void local build Args((Cell,Int));
static Void local buildGuards Args((List,Int));
static Int local buildLoc Args((List,Int));
static Void local make Args((Cell,Int,Label,Label));
static Void local makeCond Args((Cell,Cell,Cell,Int,Label,Label));
static Void local testGuard Args((Pair,Int,Label,Label,Label));
static Void local testCase Args((Pair,Int,Label,Label,Label));
static Void local analyseAp Args((Cell));
static Void local buildAp Args((Cell,Int,Label,Bool));
static Void local evalString Args((Cell));
static Void local run Args((Addr,StackPtr));
/* --------------------------------------------------------------------------
* Assembler: (Low level, instruction code storage)
* ------------------------------------------------------------------------*/
static Addr startInstr; /* first instruction after START */
static Addr lastInstr; /* last instr written (for peephole */
/* optimisations etc.) */
static Addr noMatch; /* address of a single FAIL instr */
static Void local instrNone(opc) /* Opcode with no operands */
Instr opc; {
lastInstr = getMem(1);
instrAt(lastInstr) = opc;
}
static Void local instrInt(opc,n) /* Opcode with integer operand */
Instr opc;
Int n; {
lastInstr = getMem(2);
instrAt(lastInstr) = opc;
intAt(lastInstr+1) = n;
}
static Void local instrFloat(opc,fl) /* Opcode with Float operand */
Instr opc;
Float fl; {
lastInstr = getMem(2);
instrAt(lastInstr) = opc;
floatAt(lastInstr+1) = fl;
}
static Void local instrCell(opc,c) /* Opcode with Cell operand */
Instr opc;
Cell c; {
lastInstr = getMem(2);
instrAt(lastInstr) = opc;
cellAt(lastInstr+1) = c;
}
static Void local instrText(opc,t) /* Opcode with Text operand */
Instr opc;
Text t; {
lastInstr = getMem(2);
instrAt(lastInstr) = opc;
textAt(lastInstr+1) = t;
}
static Void local instrLab(opc,l) /* Opcode with label operand */
Instr opc;
Label l; {
lastInstr = getMem(2);
instrAt(lastInstr) = opc;
labAt(lastInstr+1) = l;
if (l<0)
internal("bad Label");
}
static Void local instrIntLab(opc,n,l) /* Opcode with int, label operands */
Instr opc;
Int n;
Label l; {
lastInstr = getMem(3);
instrAt(lastInstr) = opc;
intAt(lastInstr+1) = n;
labAt(lastInstr+2) = l;
if (l<0)
internal("bad Label");
}
static Void local instrCellLab(opc,c,l)/* Opcode with cell, label operands */
Instr opc;
Cell c;
Label l; {
lastInstr = getMem(3);
instrAt(lastInstr) = opc;
cellAt(lastInstr+1) = c;
labAt(lastInstr+2) = l;
if (l<0)
internal("bad Label");
}
/* --------------------------------------------------------------------------
* Main low level assembler control: (includes label assignment and fixup)
*
* Labels are used as a simple form of continuation during the code gen:
* RUNON => produce code which does not make jump at end of construction
* UPDRET => produce code which performs UPDATE 0, RETURN at end
* VALRET => produce code which performs RETURN at end
* other(d) => produce code which branches to label d at end
* ------------------------------------------------------------------------*/
static Label nextLab; /* next label number to allocate */
#define SHOULDNTFAIL (-1)
#define RUNON (-2)
#define UPDRET (-3)
#define VALRET (-4)
static Addr fixups[NUM_FIXUPS]; /* fixup table maps Label -> Addr*/
#define atLabel(n) fixups[n] = getMem(0)
#define endLabel(d,l) if (d==RUNON) atLabel(l)
#define fix(a) addrAt(a) = fixups[labAt(a)]
static Void local asSTART() { /* initialise assembler */
fixups[0] = noMatch;
nextLab = 1;
startInstr = getMem(0);
lastInstr = startInstr-1;
}
static Label local newLabel(d) /* allocate new label */
Label d; {
if (d==RUNON) {
if (nextLab>=NUM_FIXUPS) {
ERROR(0) "Compiled code too complex"
EEND;
}
return nextLab++;
}
return d;
}
static Void local asEND() { /* Fix addresses in assembled code */
Addr pc = startInstr;
while (pc<=lastInstr)
switch (instrAt(pc)) {
case iEVAL : /* opcodes taking no arguments */
case iFAIL :
case iRETURN : pc++;
break;
case iGOTO : fix(pc+1); /* opcodes taking one argument */
case iSETSTK :
case iALLOC :
case iSLIDE :
case iROOT :
case iDICT :
case iLOAD :
case iCELL :
case iCHAR :
case iINT :
case iFLOAT :
case iSTRING :
case iMKAP :
case iUPDATE :
case iUPDAP : pc+=2;
break;
case iINTGE : /* opcodes taking two arguments */
case iINTEQ :
case iINTDV :
case iTEST : fix(pc+2);
pc+=3;
break;
default : internal("fixAddrs");
}
}
/* --------------------------------------------------------------------------
* Assembler Opcodes: (includes simple peephole optimisations)
* ------------------------------------------------------------------------*/
#define asINTEGER(n) instrInt(iINT,n)
#define asFLOAT(fl) instrFloat(iFLOAT,fl)
#define asSTRING(t) instrText(iSTRING,t)
#define asCHAR(n) instrInt(iCHAR,n)
#define asLOAD(n) instrInt(iLOAD,n)
#define asALLOC(n) instrInt(iALLOC,n)
#define asROOT(n) instrInt(iROOT,n)
#define asSETSTK(n) instrInt(iSETSTK,n)
#define asEVAL() instrNone(iEVAL)
#define asRETURN() instrNone(iRETURN)
#define asCELL(c) instrCell(iCELL,c)
#define asTEST(c,l) instrCellLab(iTEST,c,l)
#define asINTGE(n,l) instrIntLab(iINTGE,n,l)
#define asINTEQ(n,l) instrIntLab(iINTEQ,n,l)
#define asINTDV(n,l) instrIntLab(iINTDV,n,l)
#define asFAIL() instrNone(iFAIL)
static Void local asDICT(n) /* pick element of dictionary */
Int n; {
/* Sadly, the following optimisation cannot be used unless CELL references
* in compiled code are garbage collected (and possibly modified when cell
* indirections are found).
*
* if (instrAt(lastInstr)==iCELL)
* -- Peephole optimisation: CELL {dict m};DICT n ==> CELL dict(m+n)
* if (whatIs(cellAt(lastInstr+1))==DICTCELL)
* cellAt(lastInstr+1) = dict(dictOf(cellAt(lastInstr+1))+n);
* else
* internal("asDICT");
* else ...
*/
if (n!=0) /* optimisation:DICT 0 has no use */
instrInt(iDICT,n); /* for std dictionary construction */
}
static Void local asSLIDE(n) /* Slide results down stack */
Int n; {
if (instrAt(lastInstr)==iSLIDE) /* Peephole optimisation: */
intAt(lastInstr+1)+=n; /* SLIDE n;SLIDE m ===> SLIDE (n+m)*/
else
instrInt(iSLIDE,n);
}
static Void local asMKAP(n) /* Make application nodes ... */
Int n; {
if (instrAt(lastInstr)==iMKAP) /* Peephole optimisation: */
intAt(lastInstr+1)+=n; /* MKAP n; MKAP m ===> MKAP (n+m) */
else
instrInt(iMKAP,n);
}
static Void local asUPDATE(n) /* Update node ... */
Int n; {
if (instrAt(lastInstr)==iMKAP) { /* Peephole optimisations: */
if (intAt(lastInstr+1)>1) { /* MKAP (n+1); UPDATE p */
intAt(lastInstr+1)--; /* ===> MKAP n; UPDAP p */
instrInt(iUPDAP,n);
}
else {
instrAt(lastInstr) = iUPDAP;
intAt(lastInstr+1) = n; /* MKAP 1; UPDATE p ===> UPDAP p */
}
}
else
instrInt(iUPDATE,n);
}
static Void local asGOTO(l) /* End evaluation of expr in manner*/
Label l; { /* indicated by label l */
switch (l) {
case UPDRET : asUPDATE(0);
case VALRET : asRETURN();
case RUNON : break;
default : instrLab(iGOTO,l);
break;
}
}
/* --------------------------------------------------------------------------
* Dissassembler:
* ------------------------------------------------------------------------*/
#ifdef DEBUG_CODE
#define printAddr(a) printf("0x%04X",a)/* printable representation of Addr */
static Void local dissassemble(pc,end) /* print dissassembly of code */
Addr pc;
Addr end; {
while (pc<=end) {
printAddr(pc);
printf("\t");
switch (instrAt(pc)) {
case iLOAD : pc = dissInt(pc,"LOAD"); break;
case iCELL : pc = dissCell(pc,"CELL"); break;
case iCHAR : pc = dissInt(pc,"CHAR"); break;
case iINT : pc = dissInt(pc,"INT"); break;
case iFLOAT : pc = dissFloat(pc,"FLOAT"); break;
case iSTRING : pc = dissText(pc,"STRING"); break;
case iMKAP : pc = dissInt(pc,"MKAP"); break;
case iUPDATE : pc = dissInt(pc,"UPDATE"); break;
case iUPDAP : pc = dissInt(pc,"UPDAP"); break;
case iEVAL : pc = dissNone(pc,"EVAL"); break;
case iRETURN : pc = dissNone(pc,"RETURN"); break;
case iINTGE : pc = dissIntAddr(pc,"INTGE"); break;
case iINTEQ : pc = dissIntAddr(pc,"INTEQ"); break;
case iINTDV : pc = dissIntAddr(pc,"INTDV"); break;
case iTEST : pc = dissCellAddr(pc,"TEST"); break;
case iGOTO : pc = dissAddr(pc,"GOTO"); break;
case iSETSTK : pc = dissInt(pc,"SETSTK"); break;
case iALLOC : pc = dissInt(pc,"ALLOC"); break;
case iSLIDE : pc = dissInt(pc,"SLIDE"); break;
case iROOT : pc = dissInt(pc,"ROOT"); break;
case iDICT : pc = dissInt(pc,"DICT"); break;
case iFAIL : pc = dissNone(pc,"FAIL"); break;
default : internal("unknown instruction");
}
}
}
static Void local printCell(c) /* printable representation of Cell */
Cell c; {
if (isName(c))
printf("%s",textToStr(name(c).text));
else
printf("$%d",c);
}
static Addr local dissNone(pc,s) /* dissassemble instr no args */
Addr pc;
String s; {
printf("%s\n",s);
return pc+1;
}
static Addr local dissInt(pc,s) /* dissassemble instr with Int arg */
Addr pc;
String s; {
printf("%s\t%d\n",s,intAt(pc+1));
return pc+2;
}
static Addr local dissFloat(pc,s) /* dissassemble instr with Float arg*/
Addr pc;
String s; {
printf("%s\t%s\n",s,floatToString(floatAt(pc+1)));
return pc+2;
}
static Addr local dissCell(pc,s) /* dissassemble instr with Cell arg */
Addr pc;
String s; {
printf("%s\t",s);
printCell(cellAt(pc+1));
printf("\n");
return pc+2;
}
static Addr local dissText(pc,s) /* dissassemble instr with Text arg */
Addr pc;
String s; {
printf("%s\t%s\n",s,textToStr(textAt(pc+1)));
return pc+2;
}
static Addr local dissAddr(pc,s) /* dissassemble instr with Addr arg */
Addr pc;
String s; {
printf("%s\t",s);
printAddr(addrAt(pc+1));
printf("\n");
return pc+2;
}
static Addr local dissIntAddr(pc,s) /* dissassemble instr with Int/Addr */
Addr pc;
String s; {
printf("%s\t%d\t",s,intAt(pc+1));
printAddr(addrAt(pc+2));
printf("\n");
return pc+3;
}
static Addr local dissCellAddr(pc,s) /* dissassemble instr with Cell/Addr*/
Addr pc;
String s; {
printf("%s\t",s);
printCell(cellAt(pc+1));
printf("\t");
printAddr(addrAt(pc+2));
printf("\n");
return pc+3;
}
#endif
/* --------------------------------------------------------------------------
* Compile expression to code which will build expression without any
* evaluation.
* ------------------------------------------------------------------------*/
static Void local build(e,s) /* Generate code which will build an*/
Cell e; /* instance of given expression but */
Int s; { /* perform no evaluation */
Int n;
switch (whatIs(e)) {
case LETREC : n = buildLoc(fst(snd(e)),s);
build(snd(snd(e)),s+n);
asSLIDE(n);
break;
case FATBAR : build(snd(snd(e)),s);
build(fst(snd(e)),s+1);
asCELL(nameFatbar);
asMKAP(2);
break;
case COND : build(thd3(snd(e)),s);
build(snd3(snd(e)),s+1);
build(fst3(snd(e)),s+2);
asCELL(nameIf);
asMKAP(3);
break;
case GUARDED : buildGuards(snd(e),s);
break;
case AP : /*build(snd(e),s);
build(fst(e),s+1);
asMKAP(1);*/
buildAp(e,s,SHOULDNTFAIL,FALSE);
break;
case UNIT :
case TUPLE :
case NAME : asCELL(e);
break;
case DICTCELL : asCELL(dict(dictOf(e))); /* see comments for*/
break; /* DICTCELL in make*/
/* function below */
case INTCELL : asINTEGER(intOf(e));
break;
case FLOATCELL : asFLOAT(floatOf(e));
break;
case STRCELL : asSTRING(textOf(e));
break;
case CHARCELL : asCHAR(charOf(e));
break;
case OFFSET : asLOAD(offsetOf(e));
break;
default : internal("build");
}
}
static Void local buildGuards(gs,s) /* Generate code to compile list */
List gs; /* of guards to a conditional expr */
Int s; { /* without evaluation */
if (isNull(gs)) {
asCELL(nameFail);
}
else {
buildGuards(tl(gs),s);
build(snd(hd(gs)),s+1);
build(fst(hd(gs)),s+2);
asCELL(nameIf);
asMKAP(3);
}
}
static Int local buildLoc(vs,s) /* Generate code to build local var */
List vs; /* bindings on stack, with no eval */
Int s; {
Int n = length(vs);
Int i;
asALLOC(n);
for (i=1; i<=n; i++) {
build(hd(vs),s+n);
asUPDATE(s+i);
vs = tl(vs);
}
return n;
}
/* --------------------------------------------------------------------------
* Compile expression to code which will build expression evaluating guards
* and testing cases to avoid building complete graph.
* ------------------------------------------------------------------------*/
#define makeTests(ct,tests,s,f,d) { Label l1 = newLabel(d); \
List xs = tests; \
while (nonNull(tl(xs))) { \
Label l2 = newLabel(RUNON); \
ct(hd(xs),s,f,l2,l1); \
atLabel(l2); \
xs = tl(xs); \
} \
ct(hd(xs),s,f,f,d); \
endLabel(d,l1); \
}
static Void local make(e,s,f,d) /* Construct code to build e, given */
Cell e; /* s arguments on stack, and branch */
Int s; /* to f on failure, d on completion */
Label f;
Label d; {
switch (whatIs(e)) {
case LETREC : { Int n = buildLoc(fst(snd(e)),s);
make(snd(snd(e)),s+n,f,RUNON);
asSLIDE(n);
asGOTO(d);
}
break;
case FATBAR : { Label l1 = newLabel(RUNON);
Label l2 = newLabel(d);
make(fst(snd(e)),s,l1,l2);
atLabel(l1);
asSETSTK(s);
make(snd(snd(e)),s,f,l2);
endLabel(d,l2);
}
break;
case COND : makeCond(fst3(snd(e)),
snd3(snd(e)),
thd3(snd(e)),s,f,d);
break;
case CASE : make(fst(snd(e)),s,SHOULDNTFAIL,RUNON);
asEVAL();
makeTests(testCase,snd(snd(e)),s,f,d);
break;
case GUARDED : makeTests(testGuard,snd(e),s,f,d);
break;
case AP : if (andorOptimise) {
Cell h = getHead(e);
if (h==nameAnd && argCount==2) {
/* x && y ==> if x then y else False */
makeCond(arg(fun(e)),arg(e),nameFalse,s,f,d);
break;
}
else if (h==nameOr && argCount==2) {
/* x || y ==> if x then True else y */
makeCond(arg(fun(e)),nameTrue,arg(e),s,f,d);
break;
}
}
/*build(snd(e),s);
make(fst(e),s+1,f,RUNON);
asMKAP(1);*/
buildAp(e,s,f,TRUE);
asGOTO(d);
break;
case UNIT :
case TUPLE :
case NAME : asCELL(e);
asGOTO(d);
break;
/* for dict cells, ensure that CELL referred to in the code is the */
/* dictionary cell at the head of the dictionary; not just a copy */
case DICTCELL : asCELL(dict(dictOf(e)));
asGOTO(d);
break;
case INTCELL : asINTEGER(intOf(e));
asGOTO(d);
break;
case FLOATCELL : asFLOAT(floatOf(e));
asGOTO(d);
break;
case STRCELL : asSTRING(textOf(e));
asGOTO(d);
break;
case CHARCELL : asCHAR(charOf(e));
asGOTO(d);
break;
case OFFSET : asLOAD(offsetOf(e));
asGOTO(d);
break;
default : internal("make");
}
}
static Void local makeCond(i,t,e,s,f,d) /* Build code for conditional */
Cell i,t,e;
Int s;
Label f;
Label d; {
Label l1 = newLabel(RUNON);
Label l2 = newLabel(d);
make(i,s,f,RUNON);
asEVAL();
asTEST(nameTrue,l1);
make(t,s,f,l2);
atLabel(l1);
make(e,s,f,l2);
endLabel(d,l2);
}
static Void local testGuard(g,s,f,cf,d) /* Produce code for guard */
Pair g;
Int s;
Label f;
Label cf;
Label d; {
make(fst(g),s,SHOULDNTFAIL,RUNON);
asEVAL();
asTEST(nameTrue,cf);
make(snd(g),s,f,d);
}
static Void local testCase(c,s,f,cf,d) /* Produce code for guard */
Pair c;
Int s; /* labels determine where to go if:*/
Label f; /* match succeeds, but rest fails */
Label cf; /* this match fails */
Label d; {
switch (whatIs(fst(c))) {
case INTCELL : asINTEQ(intOf(fst(c)),cf);
break;
case ADDPAT : asINTGE(intValOf(fst(c)),cf);
break;
case MULPAT : asINTDV(intValOf(fst(c)),cf);
break;
default : asTEST(fst(c),cf);
break;
}
make(snd(c),s+discrArity(fst(c)),f,d);
}
/* --------------------------------------------------------------------------
* We frequently encounter functions which call themselves recursively with
* a number of initial arguments preserved:
* e.g. (map f) [] = []
* (map f) (x:xs) = f x : (map f) xs
* Lambda lifting, in particular, is likely to introduce such functions.
* Rather than reconstructing a new instance of the recursive function and
* it's arguments, we can extract the relevant portion of the root of the
* current redex.
*
* The following functions implement this optimisation.
* ------------------------------------------------------------------------*/
static Int nonRoots; /* #args which can't get from root */
static Int rootPortion; /* portion of root used ... */
static Name definingName; /* name of func being defined,if any*/
static Int definingArity; /* arity of definingName */
static Void local analyseAp(e) /* Determine if any portion of an */
Cell e; { /* application can be built using a */
if (isAp(e)) { /* portion of the root */
analyseAp(fun(e));
if (nonRoots==0 && rootPortion>1
&& isOffset(arg(e))
&& offsetOf(arg(e))==rootPortion-1)
rootPortion--;
else
nonRoots++;
}
else if (e==definingName)
rootPortion = definingArity+1;
else
rootPortion = 0;
}
static Void local buildAp(e,s,f,str) /* Build application, making use of */
Cell e; /* root optimisation if poss. */
Int s;
Label f;
Bool str; {
Int nr, rp, i;
nonRoots = 0;
analyseAp(e);
nr = nonRoots;
rp = rootPortion;
for (i=0; i<nr; ++i) {
build(arg(e),s+i);
e = fun(e);
}
if (isSelect(e)) {
if (selectOf(e)>0)
asDICT(selectOf(e));
}
else {
if (isName(e) && name(e).defn==MFUN) {
asDICT(name(e).number);
nr--; /* AP node for member function need never be built */
}
else {
if (0<rp && rp<=definingArity)
asROOT(rp-1);
else
if (str)
make(e,s+nr,f,RUNON);
else
build(e,s+nr);
}
if (nr>0)
asMKAP(nr);
}
}
/* --------------------------------------------------------------------------
* Code generator entry point:
* ------------------------------------------------------------------------*/
Addr codeGen(n,arity,e) /* Generate code for expression e, */
Name n; /* treating return value of CAFs */
Int arity; /* differently to functs with args */
Cell e; {
definingName = n;
definingArity = arity;
asSTART();
#ifdef DEBUG_CODE
printf("------------------\n");
if (nonNull(n)) printf("name=%s\n",textToStr(name(n).text));
printf("Arity = %d\n",arity);
printf("codeGen = "); printExp(stdout,e); putchar('\n');
#endif
make(e,arity,noMatch,(arity>0 ? UPDRET : VALRET));
asEND();
#ifdef DEBUG_CODE
dissassemble(startInstr,lastInstr);
printf("------------------\n");
#endif
return startInstr;
}
/* --------------------------------------------------------------------------
* Evaluator:
* ------------------------------------------------------------------------*/
Int whnfArgs; /* number of arguments of whnf term */
Cell whnfHead; /* head cell of term in whnf */
Int whnfInt; /* value of INTCELL (in whnf) */
Float whnfFloat; /* value of FLOATCELL (in whnf) */
Long numReductions; /* number of reductions counted */
static Cell errorRedex; /* irreducible error expression */
static jmp_buf *evalError = 0; /* jump buffer for eval errors */
#ifdef DEBUG_RUN
static evalCnt=0;
#endif
Void eval(n) /* Graph reduction evaluator */
Cell n; {
StackPtr base = sp;
Int ar;
#ifdef DEBUG_RUN
Int keepEvalCnt = evalCnt++;
printf("%-5d Eval: ",keepEvalCnt);
printExp(stdout,n);
putchar('\n');
#endif
unw:switch (whatIs(n)) { /* unwind spine of application */
case AP : push(n);
n = fun(n);
goto unw;
case INDIRECT : n = arg(n);
allowBreak();
goto unw;
case NAME : ar = name(n).arity;
#ifdef DEBUG_RUN
printf("Reducing %s:\n",textToStr(name(n).text));
#endif
if (name(n).defn!=CFUN && sp-base>=ar) {
allowBreak();
if (ar>0) { /* fn with args*/
StackPtr root;
push(NIL); /* rearrange */
root = sp;
do {
stack(root) = arg(stack(root-1));
--root;
} while (--ar>0);
if (name(n).primDef) /* reduce */
(*name(n).primDef)(root);
else
run(name(n).code,root);
numReductions++;
sp = root; /* continue... */
n = pop();
}
else { /* CAF */
if (isNull(name(n).defn)) {/* build CAF */
push(n); /* save CAF */
if (name(n).primDef)
(*name(n).primDef)(sp);
else
run(name(n).code,sp);
numReductions++;
name(n).defn = pop();
drop(); /* drop CAF */
}
n = name(n).defn; /*already built*/
if (sp>base)
fun(top()) = n;
}
goto unw;
}
break;
case INTCELL : whnfInt = intOf(n);
break;
case FLOATCELL : whnfFloat = floatOf(n);
break;
case STRCELL : evalString(n);
goto unw;
case FILECELL : evalFile(n);
goto unw;
}
whnfHead = n; /* rearrange components of term on */
whnfArgs = sp - base; /* stack, now in whnf ... */
for (ar=whnfArgs; ar>0; ar--) {
fun(stack(base+ar)) = n;
n = stack(base+ar);
stack(base+ar) = arg(n);
}
#ifdef DEBUG_RUN
printf("%-5d Whnf: ",keepEvalCnt);
printExp(stdout,n);
putchar('\n');
#endif
}
Void unwind(n) /* unwind spine of application; */
Cell n; { /* like eval except that we always */
whnfArgs = 0; /* treat the expression n as if it */
/* were already in whnf. */
unw:switch (whatIs(n)) {
case AP : push(arg(n));
whnfArgs++;
n = fun(n);
goto unw;
case INDIRECT : n = arg(n);
allowBreak();
goto unw;
case INTCELL : whnfInt = intOf(n);
break;
case FLOATCELL : whnfFloat = floatOf(n);
break;
case STRCELL : evalString(n);
goto unw;
}
whnfHead = n;
}
static Void local evalString(n) /* expand STRCELL at node n */
Cell n; {
Text t = textOf(n);
Int c = textToStr(t)[0];
if (c==0) {
fst(n) = INDIRECT;
snd(n) = nameNil;
return;
}
else if (c=='\\') {
c = textToStr(++t)[0];
if (c!='\\')
c = 0;
}
fst(n) = consChar(c);
snd(n) = mkStr(++t);
}
static Void local run(pc,root) /* execute code beginning at given */
Addr pc; /* address with local stack starting*/
StackPtr root; { /* at given root offset */
Cell t;
Int i;
for (;;)
switch (instrAt(pc)) {
case iLOAD : push(stack(root+intAt(pc+1)));/* load from stack*/
pc+=2;
continue;
case iCELL : push(cellAt(pc+1)); /* load const Cell*/
pc+=2;
continue;
case iCHAR : push(mkChar(intAt(pc+1))); /* load char const*/
pc+=2;
continue;
case iINT : push(mkInt(intAt(pc+1))); /* load int const */
pc+=2;
continue;
case iFLOAT : push(mkFloat(floatAt(pc+1))); /* load float cnst*/
pc+=2;
continue;
case iSTRING : push(mkStr(textAt(pc+1))); /* load str const */
pc+=2;
continue;
case iMKAP : t=pushed(0); /* make AP nodes */
for (i=intAt(pc+1); i>0; --i) {
drop();
t=ap(t,pushed(0));
}
pushed(0)=t;
pc+=2;
continue;
case iUPDATE : t=stack(root+intAt(pc+1)); /* update cell ...*/
fst(t) = INDIRECT;
snd(t) = pop();
pc+=2;
continue;
case iUPDAP : t=stack(root+intAt(pc+1)); /* update AP node */
fst(t) = pop();
snd(t) = pop();
pc+=2;
continue;
case iEVAL : eval(pop()); /* evaluate top() */
pc++;
continue;
case iRETURN : return; /* terminate */
case iINTGE : if (whnfInt>=intAt(pc+1)) { /* test integer >=*/
push(mkInt(whnfInt-intAt(pc+1)));
pc+=3;
}
else
pc=addrAt(pc+2);
continue;
case iINTEQ : if (whnfInt==intAt(pc+1)) /* test integer ==*/
pc+=3;
else
pc=addrAt(pc+2);
continue;
case iINTDV : if (whnfInt>=0 && /* test for mult */
(whnfInt%intAt(pc+1)==0)) {
push(mkInt(whnfInt/intAt(pc+1)));
pc+=3;
}
else
pc=addrAt(pc+2);
continue;
case iTEST : if (whnfHead==cellAt(pc+1)) /* test for cell */
pc+=3;
else
pc=addrAt(pc+2);
continue;
case iGOTO : pc=addrAt(pc+1); /* goto label */
continue;
case iSETSTK : sp=root+intAt(pc+1); /* set stack ptr */
pc+=2;
continue;
case iALLOC : for (i=intAt(pc+1); i>0; --i) /* alloc loc vars */
push(ap(NIL,NIL));
pc+=2;
continue;
case iDICT : top() = dict(dictOf(top())+intAt(pc+1));
pc+=2; /* dict lookup */
continue;
case iROOT : t=stack(root); /* partial root */
while (fst(t)==INDIRECT) {
allowBreak();
t = arg(t);
}
for (i=intAt(pc+1); i>0; --i) {
t = fun(t);
while (fst(t)==INDIRECT) {
allowBreak();
t = arg(t);
}
}
pc+=2;
push(t);
continue;
case iSLIDE : pushed(intAt(pc+1)) = top(); /* remove loc vars*/
sp-=intAt(pc+1);
pc+=2;
continue;
case iFAIL : evalFails(root); /* cannot reduce */
break;
default : internal("illegal instruction");
break;
}
}
Cell evalWithNoError(e) /* Evaluate expression, returning */
Cell e; { /* NIL if successful, irreducible */
Cell badRedex; /* expression if not... */
jmp_buf *oldCatch = evalError;
#if JMPBUF_ARRAY
jmp_buf catch[1];
evalError = catch;
if (setjmp(catch[0])==0) {
eval(e);
badRedex = NIL;
}
else
badRedex = errorRedex;
#else
jmp_buf catch;
evalError = &catch;
if (setjmp(catch)==0) {
eval(e);
badRedex = NIL;
}
else
badRedex = errorRedex;
#endif
evalError = oldCatch;
return badRedex;
}
Void evalFails(root) /* Eval of current redex fails */
StackPtr root; {
errorRedex = stack(root); /* get error & bypass indirections */
while (isPair(errorRedex) && fst(errorRedex)==INDIRECT)
errorRedex = snd(errorRedex);
if (failOnError)
abandon("evaluation",errorRedex);
else if (evalError)
longjmp(*evalError,1);
else
internal("uncaught eval error");
}
Cell graphForExp() { /* Build graph for expression to be*/
clearStack(); /* reduced... */
run(inputCode,sp);
return pop();
}
/* --------------------------------------------------------------------------
* Machine control:
* ------------------------------------------------------------------------*/
Void machine(what)
Int what; {
switch (what) {
case RESET : break;
case MARK : break;
case INSTALL : machine(RESET);
memory = (Memory)farCalloc(NUM_ADDRS,sizeof(MemCell));
if (memory==0)
internal("Cannot allocate program memory");
instrNone(iFAIL);
noMatch = lastInstr;
break;
}
}
/* ------------------------------------------------------------------------*/